package com.scau.jansing.utils;

import org.apache.commons.lang.StringUtils;

import java.io.File;
import java.io.FileNotFoundException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

/**
 * 这里为了简单，不设置注解，只要类在指定包内，就生成单例，并用类名作为key<br/>
 * 实际上应该使用注解，并注明是否单例，以及key值
 * Created by jansing on 2015/11/18.
 */
public class Container {
    //key = @Bean.value;  value = object
    private static Map<String, Object> objectMap = new HashMap<>();


    /**
     * 扫描某个package下的所有类，判断是否需要生成单例保存，其中key=类名
     *
     * @param packagePath com.scau.jansing
     * @return
     */
    public static boolean scanPackage(String packagePath) throws Exception {
        String pack = FileUtils.getCodePath(packagePath);
        File dir = new File(pack);
        try {
            if (!dir.exists()) {
                throw new FileNotFoundException("路径不存在");
            }
            List<File> fileList = new ArrayList<>(Arrays.asList(dir.listFiles()));
            File clazzFile;
            for (int i = 0; i < fileList.size(); i++) {
                clazzFile = fileList.get(i);
                if (clazzFile.isDirectory()) {
                    fileList.addAll(Arrays.asList(clazzFile.listFiles()));
                } else {

                    Class temp = Class.forName(FileUtils.getClazzName(clazzFile));
                    String beanKey = getClazzBeanName(temp);
                    if (StringUtils.isNotBlank(beanKey)) {
                        objectMap.put(beanKey, temp.newInstance());
                    }
                }
            }
            //在实例化结束后，需要对这些对象注入属性
            IOC();
            return true;

        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 暂时有两种想法：<br/>
     * 1、在scanPackage()中创建单例时注入，此时有一个问题，即要注入的对象还没有实例化<br/>
     * 2、在scanPackage()完成后调用，此时用了好多反射，不知道对性能影响有多大，<br/>
     * 不过考虑到是初始化的时候调用的，属一次性方法，性能应该没关系。所以采用方法2。
     */
    public static void IOC() throws Exception {
        Iterator<String> iterator = objectMap.keySet().iterator();
        while (iterator.hasNext()) {
            String key = iterator.next();
            Object obj = objectMap.get(key);
            // 如果是继承来的属性呢？
            // 不考虑，因为继承来的属性不是private的
            Field[] fields = obj.getClass().getDeclaredFields();
            for (int i = 0; i < fields.length; i++) {
                try {
                    String fieldBeanName = getFieldBeanName(fields[i]);
                    if (StringUtils.isNotBlank(fieldBeanName)) {
                        if(getBean(fieldBeanName)==null){
                            throw new Exception("Container中没有"+fieldBeanName+"对象");
                        }
                        fields[i].setAccessible(true);
                        fields[i].set(obj, getBean(fieldBeanName));
                    }
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    public static Object getBean(String key) {
        return objectMap.get(key);
    }


    /**
     * 判断一个注解是否是需要初始化单例的注解，即传入的注解是否被MyAnnotation注解，如Bean、Controller
     *
     * @param isBeanAnnotation
     * @return
     */
    public static boolean isBeanAnnotation(Class isBeanAnnotation) {
        Iterator<Annotation> annotationIterator =
                Arrays.asList(isBeanAnnotation.getDeclaredAnnotations()).iterator();
        return isContainsAnnotation(annotationIterator, MyAnnotation.class) == null ? false : true;
    }

    /**
     * 判断iterator中是否存在destAnnotation，如果存在，则返回该对象
     *
     * @param annotationIterator
     * @param destAnnotation
     * @return
     */
    private static Annotation isContainsAnnotation(Iterator<Annotation> annotationIterator, Class destAnnotation) {
        Annotation annotation;
        while (annotationIterator.hasNext()) {
            annotation = annotationIterator.next();
            if (annotation.annotationType().equals(destAnnotation)) {
                return annotation;
            }
        }
        return null;
    }

    /**
     * 传入clazz，如果是需实例化保存的（即有有MyAnnotation注解的注解），
     * 如果其value不为空，则返回value，否则返回其类名；<br/>
     * 如果没有这样的注解，则返回null
     *
     * @param clazz 可能需实例化保存类
     * @return key
     */
    public static String getClazzBeanName(Class clazz) {
        Iterator<Annotation> annotationIterator = Arrays.asList(clazz.getDeclaredAnnotations()).iterator();
        return getMyAnnotationValue(annotationIterator, CommonUtils.lowerTheFirstCase(clazz.getSimpleName()));
    }

    /**
     * 传入field，如果是需注入的（即有有MyAnnotation注解的注解），
     * 如果其value不为空，则返回value，否则返回其类名；<br/>
     * 如果没有这样的注解，则返回null
     *
     * @param field
     * @return
     */
    public static String getFieldBeanName(Field field) {
        try {
            Iterator<Annotation> annotationIterator = Arrays.asList(field.getDeclaredAnnotations()).iterator();
            Annotation annotation = isContainsAnnotation(annotationIterator, Resource.class);
            if (annotation != null) {
                return getAnnotationValue(annotation, field.getName());
            }
        } catch (NoSuchMethodException e) {
            //注解没有value属性？
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 抽象出获取被MyAnnotation修饰的注解的value()
     *
     * @param annotationIterator
     * @param defaultValue
     * @return 如果是，则返回value值/(如果value=""时返回)defaultValue；否则返回null
     */
    public static String getMyAnnotationValue(Iterator<Annotation> annotationIterator, String defaultValue) {
        Annotation annotation;
        try {
            while (annotationIterator.hasNext()) {
                annotation = annotationIterator.next();
                if (Container.isBeanAnnotation(annotation.annotationType())) {
                    return getAnnotationValue(annotation, defaultValue);
                }
            }
        } catch (NoSuchMethodException e) {
            //注解没有value属性？
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取Annotation的value值
     *
     * @param annotation
     * @param defaultValue
     * @return 返回value值/(如果value=""时返回)defaultValue；
     * @throws NoSuchMethodException
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    private static String getAnnotationValue(Annotation annotation, String defaultValue)
            throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Method method = annotation.getClass().getMethod("value");
        String value = (String) method.invoke(annotation);
        if (StringUtils.isBlank(value)) {
            return defaultValue;
        } else {
            return value;
        }
    }
}
